#include <stdio.h>

#include "cmsis_os2.h"
#include "ohos_init.h"
#include <cJSON.h>
#include <dtls_al.h>
#include <mqtt_al.h>
#include <oc_mqtt_al.h>
#include <oc_mqtt_profile.h>

#include "iot_gpio.h"
#include "iot_gpio_ex.h"
#include "wifi_connect.h"
#include "wifi_config.h"
#include "aht20.h"
#include "mq2.h"
#include "beep.h"
#include "led.h"
#include "key.h"

osMessageQueueId_t mid_MsgQueue;
typedef enum
{
    en_msg_cmd = 0,
    en_msg_report,
    en_msg_conn,
    en_msg_disconn,
} en_msg_type_t;

typedef struct
{
    char *request_id;
    char *payload;
} cmd_t;

typedef struct
{
    int temp;
    int hum;
    int ppm;
    int lum;
    int led;
    int motor;
    int rel;
} report_t;

typedef struct
{
    en_msg_type_t msg_type;
    union
    {
        cmd_t cmd;
        report_t report;
    } msg;
} app_msg_t;

typedef struct
{
    osMessageQueueId_t app_msg;
    int connected;
    int led;
    int beep;
    int motor;
    int rel;
} app_cb_t;
static app_cb_t g_app_cb;

static void deal_report_msg(report_t *report)
{
    oc_mqtt_profile_service_t service;
    oc_mqtt_profile_kv_t temperature;
    oc_mqtt_profile_kv_t humidity;
    oc_mqtt_profile_kv_t ppm;
    oc_mqtt_profile_kv_t beep;
    oc_mqtt_profile_kv_t led;
    oc_mqtt_profile_kv_t luminance;
    oc_mqtt_profile_kv_t motor;
    oc_mqtt_profile_kv_t rel;

    if (g_app_cb.connected != 1)
    {
        return;
    }

    service.event_time = NULL;
    service.service_id = "Environment";
    service.service_property = &temperature;
    service.nxt = NULL;

    temperature.key = "Temperature";
    temperature.value = &report->temp;
    temperature.type = EN_OC_MQTT_PROFILE_VALUE_INT;
    temperature.nxt = &humidity;

    humidity.key = "Humidity";
    humidity.value = &report->hum;
    humidity.type = EN_OC_MQTT_PROFILE_VALUE_INT;
    humidity.nxt = &ppm;

    ppm.key = "ppm";
    ppm.value = &report->ppm;
    ppm.type = EN_OC_MQTT_PROFILE_VALUE_INT;
    ppm.nxt = &beep;

    beep.key = "Beep";
    beep.value = g_app_cb.beep ? "ON" : "OFF";
    beep.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    beep.nxt = &led;

    led.key = "Led";
    led.value = g_app_cb.led ? "ON" : "OFF";
    led.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    led.nxt = &luminance;

    luminance.key = "Luminance";
    luminance.value = &report->lum;
    luminance.type = EN_OC_MQTT_PROFILE_VALUE_INT;
    luminance.nxt = &motor;

    motor.key = "Motor";
    motor.value = g_app_cb.motor ? "ON" : "OFF";
    motor.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    motor.nxt = &rel;

    rel.key = "Rel";
    rel.value = g_app_cb.rel ? "ON" : "OFF";
    rel.type = EN_OC_MQTT_PROFILE_VALUE_STRING;
    rel.nxt = NULL;

    oc_mqtt_profile_propertyreport(NULL, &service);
    return;
}

static int msg_rcv_callback(oc_mqtt_profile_msgrcv_t *msg)
{
    int ret = 0;
    char *buf;
    int buf_len;
    app_msg_t *app_msg;

    if ((msg == NULL) || (msg->request_id == NULL) || (msg->type != EN_OC_MQTT_PROFILE_MSG_TYPE_DOWN_COMMANDS))
    {
        return ret;
    }

    buf_len = sizeof(app_msg_t) + strlen(msg->request_id) + 1 + msg->msg_len + 1;
    buf = malloc(buf_len);
    if (buf == NULL)
    {
        return ret;
    }
    app_msg = (app_msg_t *)buf;
    buf += sizeof(app_msg_t);

    app_msg->msg_type = en_msg_cmd;
    app_msg->msg.cmd.request_id = buf;
    buf_len = strlen(msg->request_id);
    buf += buf_len + 1;
    memcpy_s(app_msg->msg.cmd.request_id, buf_len, msg->request_id, buf_len);
    app_msg->msg.cmd.request_id[buf_len] = '\0';

    buf_len = msg->msg_len;
    app_msg->msg.cmd.payload = buf;
    memcpy_s(app_msg->msg.cmd.payload, buf_len, msg->msg, buf_len);
    app_msg->msg.cmd.payload[buf_len] = '\0';

    ret = osMessageQueuePut(g_app_cb.app_msg, &app_msg, 0U, CONFIG_QUEUE_TIMEOUT);
    if (ret != 0)
    {
        free(app_msg);
    }

    return ret;
}

static void oc_cmdresp(cmd_t *cmd, int cmdret)
{
    oc_mqtt_profile_cmdresp_t cmdresp;

    cmdresp.paras = NULL;
    cmdresp.request_id = cmd->request_id;
    cmdresp.ret_code = cmdret;
    cmdresp.ret_name = NULL;
    (void)oc_mqtt_profile_cmdresp(NULL, &cmdresp);
}

static void deal_beep_cmd(cmd_t *cmd, cJSON *obj_root)
{
    cJSON *obj_paras;
    cJSON *obj_para;
    int cmdret;

    obj_paras = cJSON_GetObjectItem(obj_root, "paras");
    if (obj_paras == NULL)
    {
        cJSON_Delete(obj_root);
    }
    obj_para = cJSON_GetObjectItem(obj_paras, "Beep");
    if (obj_paras == NULL)
    {
        cJSON_Delete(obj_root);
    }
    if (strcmp(cJSON_GetStringValue(obj_para), "ON") == 0)
    {
        g_app_cb.beep = 1;
        Beep_On();
        printf("Beep On!\r\n");
    }
    else
    {
        g_app_cb.beep = 0;
        Beep_Off();
        printf("Beep Off!\r\n");
    }
    cmdret = 0;
    oc_cmdresp(cmd, cmdret);

    cJSON_Delete(obj_root);
    return;
}

static void deal_led_cmd(cmd_t *cmd, cJSON *obj_root)
{
    cJSON *obj_paras;
    cJSON *obj_para;
    int cmdret;

    obj_paras = cJSON_GetObjectItem(obj_root, "Paras");
    if (obj_paras == NULL)
    {
        cJSON_Delete(obj_root);
    }
    obj_para = cJSON_GetObjectItem(obj_paras, "Led");
    if (obj_paras == NULL)
    {
        cJSON_Delete(obj_root);
    }
    if (strcmp(cJSON_GetStringValue(obj_para), "ON") == 0)
    {
        g_app_cb.led = 1;
        Led_On();
        printf("Led On!\r\n");
    }
    else
    {
        g_app_cb.led = 0;
        Led_Off();
        printf("Led Off!\r\n");
    }
    cmdret = 0;
    oc_cmdresp(cmd, cmdret);

    cJSON_Delete(obj_root);
    return;
}

static void deal_motor_cmd(cmd_t *cmd, cJSON *obj_root)
{
    cJSON *obj_paras;
    cJSON *obj_para;
    int cmdret;

    obj_paras = cJSON_GetObjectItem(obj_root, "Paras");
    if (obj_paras == NULL)
    {
        cJSON_Delete(obj_root);
    }
    obj_para = cJSON_GetObjectItem(obj_paras, "Motor");
    if (obj_para == NULL)
    {
        cJSON_Delete(obj_root);
    }

    if (strcmp(cJSON_GetStringValue(obj_para), "ON") == 0)
    {
        g_app_cb.motor = 1;
        Motor_On();
        printf("Motor On!\r\n");
    }
    else
    {
        g_app_cb.motor = 0;
        Motor_Off();
        printf("Motor Off!\r\n");
    }
    cmdret = 0;
    oc_cmdresp(cmd, cmdret);

    cJSON_Delete(obj_root);
    return;
}

static void deal_cmd_msg(cmd_t *cmd)
{
    cJSON *obj_root;
    cJSON *obj_cmdname;

    int cmdret = 1;
    obj_root = cJSON_Parse(cmd->payload);
    if (obj_root == NULL)
    {
        oc_cmdresp(cmd, cmdret);
    }
    obj_cmdname = cJSON_GetObjectItem(obj_root, "command_name");
    if (obj_cmdname == NULL)
    {
        cJSON_Delete(obj_root);
    }
    if (strcmp(cJSON_GetStringValue(obj_cmdname), "Environment_Control_Beep") == 0)
    {
        deal_beep_cmd(cmd, obj_root);
    }
    else if (strcmp(cJSON_GetStringValue(obj_cmdname), "Environment_Control_Led") == 0)
    {
        deal_led_cmd(cmd, obj_root);
    }
    else if (strcmp(cJSON_GetStringValue(obj_cmdname), "Environment_Control_Motor") == 0)
    {
        deal_motor_cmd(cmd, obj_root);
    }

    return;
}

static int CloudMainTaskEntry(void)
{
    app_msg_t *app_msg;
    uint32_t ret;

    WifiConnect();
    // WifiHotspot();
    dtls_al_init();
    mqtt_al_init();
    oc_mqtt_init();

    g_app_cb.app_msg = osMessageQueueNew(MSGQUEUE_COUNT, MSGQUEUE_SIZE, NULL);
    if (g_app_cb.app_msg == NULL)
    {
        printf("Create receive msg queue failed");
    }
    oc_mqtt_profile_connect_t connect_para;
    (void)memset_s(&connect_para, sizeof(connect_para), 0, sizeof(connect_para));

    connect_para.boostrap = 0;
    connect_para.device_id = CONFIG_APP_DEVICEID;
    connect_para.device_passwd = CONFIG_APP_DEVICEPWD;
    connect_para.server_addr = CONFIG_APP_SERVERIP;
    connect_para.server_port = CONFIG_APP_SERVERPORT;
    connect_para.life_time = CONFIG_APP_LIFETIME;
    connect_para.rcvfunc = msg_rcv_callback;
    connect_para.security.type = EN_DTLS_AL_SECURITY_TYPE_NONE;
    ret = oc_mqtt_profile_connect(&connect_para);
    printf("ret = %u\n", ret);
    if ((ret == (int)en_oc_mqtt_err_ok))
    {
        g_app_cb.connected = 1;
        printf("oc_mqtt_profile_connect successd!\r\n");
    }
    else
    {
        printf("oc_mqtt_profile_connect faild!\r\n");
    }

    while (1)
    {
        app_msg = NULL;
        (void)osMessageQueueGet(g_app_cb.app_msg, (void **)&app_msg, NULL, 0xFFFFFFFF);
        if (app_msg != NULL)
        {
            switch (app_msg->msg_type)
            {
            case en_msg_cmd:
                deal_cmd_msg(&app_msg->msg.cmd);
                break;
            case en_msg_report:
                deal_report_msg(&app_msg->msg.report);
                break;
            default:
                break;
            }
            free(app_msg);
        }
    }
    return 0;
}

static int SensorTaskEntry(void)
{
    app_msg_t *app_msg;
    float humidity = 0.0f;
    float temperature = 0.0f;
    float luminance = 0.0f;
    double ppm = 0.0;
    IotGpioValue rel;

    while (1)
    {
        AHT20_GetMeasureResult(&temperature, &humidity);
        MQ2_GetData(&ppm);
        RES_GetData(&luminance);
        IoTGpioGetInputVal(IOT_IO_NAME_GPIO_8, &rel);
        g_app_cb.rel = rel;
        switch (GetGpio5Voltage())
        {
        case USER:
            Led_Off();
            g_app_cb.led = 0;
            break;
        case S1:
            Motor_On();
            Led_On();
            g_app_cb.led = 1;
            g_app_cb.motor = 1;
            break;
        case S2:
            Motor_Off();
            g_app_cb.motor = 0;
            break;
        default:
            break;
        }

        if (luminance > 3.0 && rel == 1)
        {
            Led_On();
        }
        else if ((luminance < 1.0 || rel == 0) && (g_app_cb.led == 0))
        {
            Led_Off();
        }

        if (ppm >= 2000)
        {
            Motor_On();
            Beep_On();
            TaskMsleep(500);
            Beep_Off();
        }
        else if ((ppm < 2000) && (g_app_cb.motor == 0))
        {
            Motor_Off();
        }

        printf("temp:%.2f humi:%.2f ppm:%.2f lum:%.2f %d\r\n", temperature, humidity, ppm, luminance, g_app_cb.rel);
        app_msg = malloc(sizeof(app_msg_t));
        if (app_msg != NULL)
        {
            app_msg->msg_type = en_msg_report;
            app_msg->msg.report.temp = (int)temperature;
            app_msg->msg.report.hum = (int)humidity;
            app_msg->msg.report.ppm = (int)ppm;
            app_msg->msg.report.lum = (int)luminance;
            app_msg->msg.report.led = g_app_cb.led;
            app_msg->msg.report.motor = g_app_cb.motor;
            app_msg->msg.report.rel = g_app_cb.rel;
            if (osMessageQueuePut(g_app_cb.app_msg, &app_msg, 0U, CONFIG_QUEUE_TIMEOUT != 0))
            {
                free(app_msg);
            }
        }
        TaskMsleep(2000);
    }
    return 0;
}

static void IotMainTaskEntry(void)
{
    osThreadAttr_t attr;

    attr.name = "CloudMainTaskEntry";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = 25;

    if (osThreadNew((osThreadFunc_t)CloudMainTaskEntry, NULL, &attr) == NULL)
    {
        printf("Failed to create CloudMainTaskEntry!\n");
    }
    attr.name = "SensorTaskEntry";
    attr.stack_size = 4096;
    attr.priority = 26;
    if (osThreadNew((osThreadFunc_t)SensorTaskEntry, NULL, &attr) == NULL)
    {
        printf("Failed to create SensorTaskEntry!\n");
    }
}

APP_FEATURE_INIT(IotMainTaskEntry);